<meta charset="utf-8">
(#) Appcompat Custom Widgets

!!! ERROR: Appcompat Custom Widgets
   This is an error.

Id
:   `AppCompatCustomView`
Summary
:   Appcompat Custom Widgets
Severity
:   Error
Category
:   Correctness
Platform
:   Android
Vendor
:   Android Open Source Project
Feedback
:   https://issuetracker.google.com/issues/new?component=192708
Since
:   2.3.0 (March 2017)
Affects
:   Kotlin and Java files
Editing
:   This check runs on the fly in the IDE editor
Implementation
:   [Source Code](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/AppCompatCustomViewDetector.kt)
Tests
:   [Source Code](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AppCompatCustomViewDetectorTest.kt)

In order to support features such as tinting, the appcompat library will
automatically load special appcompat replacements for the builtin
widgets. However, this does not work for your own custom views.

Instead of extending the `android.widget` classes directly, you should
instead extend one of the delegate classes in
`androidx.appcompat.widget.AppCompatTextView`.

!!! Tip
   This lint check has an associated quickfix available in the IDE.

(##) Example

Here is an example of lint warnings produced by this check:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~text
src/test/pkg/TestAppCompatSuperClasses.java:23:Error: This custom view
should extend android.support.v7.widget.AppCompatButton instead
[AppCompatCustomView]
    public class MyButton1 extends Button { // ERROR
                                   ------
src/test/pkg/TestAppCompatSuperClasses.java:28:Error: This custom view
should extend android.support.v7.widget.AppCompatButton instead
[AppCompatCustomView]
    public class MyButton2 extends Button implements Runnable { // ERROR
                                   ------
src/test/pkg/TestAppCompatSuperClasses.java:47:Error: This custom view
should extend android.support.v7.widget.AppCompatEditText instead
[AppCompatCustomView]
    public class MyEditText extends EditText { // ERROR
                                    --------
src/test/pkg/TestAppCompatSuperClasses.java:51:Error: This custom view
should extend android.support.v7.widget.AppCompatTextView instead
[AppCompatCustomView]
    public class MyTextView extends TextView { // ERROR
                                    --------
src/test/pkg/TestAppCompatSuperClasses.java:55:Error: This custom view
should extend android.support.v7.widget.AppCompatCheckBox instead
[AppCompatCustomView]
    public class MyCheckBox extends CheckBox { // ERROR
                                    --------
src/test/pkg/TestAppCompatSuperClasses.java:59:Error: This custom view
should extend android.support.v7.widget.AppCompatCheckedTextView instead
[AppCompatCustomView]
    public class MyCheckedTextView extends CheckedTextView { // ERROR
                                           ---------------
src/test/pkg/TestAppCompatSuperClasses.java:63:Error: This custom view
should extend android.support.v7.widget.AppCompatImageButton instead
[AppCompatCustomView]
    public class MyImageButton extends ImageButton { // ERROR
                                       -----------
src/test/pkg/TestAppCompatSuperClasses.java:67:Error: This custom view
should extend android.support.v7.widget.AppCompatImageView instead
[AppCompatCustomView]
    public class MyImageView extends ImageView { // ERROR
                                     ---------
src/test/pkg/TestAppCompatSuperClasses.java:71:Error: This custom view
should extend
android.support.v7.widget.AppCompatMultiAutoCompleteTextView instead
[AppCompatCustomView]
    public class MyMultiAutoCompleteTextView extends MultiAutoCompleteTextView { // ERROR
                                                     -------------------------
src/test/pkg/TestAppCompatSuperClasses.java:75:Error: This custom view
should extend android.support.v7.widget.AppCompatAutoCompleteTextView
instead [AppCompatCustomView]
    public class MyAutoCompleteTextView extends AutoCompleteTextView { // ERROR
                                                --------------------
src/test/pkg/TestAppCompatSuperClasses.java:79:Error: This custom view
should extend android.support.v7.widget.AppCompatRadioButton instead
[AppCompatCustomView]
    public class MyRadioButton extends RadioButton { // ERROR
                                       -----------
src/test/pkg/TestAppCompatSuperClasses.java:83:Error: This custom view
should extend android.support.v7.widget.AppCompatRatingBar instead
[AppCompatCustomView]
    public class MyRatingBar extends RatingBar { // ERROR
                                     ---------
src/test/pkg/TestAppCompatSuperClasses.java:87:Error: This custom view
should extend android.support.v7.widget.AppCompatSeekBar instead
[AppCompatCustomView]
    public class MySeekBar extends SeekBar { // ERROR
                                   -------
src/test/pkg/TestAppCompatSuperClasses.java:91:Error: This custom view
should extend android.support.v7.widget.AppCompatSpinner instead
[AppCompatCustomView]
    public class MySpinner extends Spinner { // ERROR
                                   -------
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Here is the source file referenced above:

`src/test/pkg/TestAppCompatSuperClasses.java`:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~java linenumbers
package test.pkg;

import android.annotation.SuppressLint;
import android.content.Context;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.CalendarView;
import android.widget.CheckBox;
import android.widget.CheckedTextView;
import android.widget.Chronometer;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.MultiAutoCompleteTextView;
import android.widget.RadioButton;
import android.widget.RatingBar;
import android.widget.SeekBar;
import android.widget.Spinner;
import android.widget.TextView;

@SuppressWarnings("unused")
public class TestAppCompatSuperClasses {
    public class MyButton1 extends Button { // ERROR
        public MyButton1(Context context) { super(context); }
    }

    // Check extends+implements list
    public class MyButton2 extends Button implements Runnable { // ERROR
        public MyButton2(Context context) { super(context); }

        @Override
        public void run() {
        }
    }

    public class MyButton3 extends MyButton1 { // Indirect: OK
        public MyButton3(Context context) { super(context); }
    }

    @SuppressLint("AppCompatCustomView")
    public class MyButton4 extends Button { // Suppressed: OK
        public MyButton4(Context context) { super(context); }
    }

    // Other widgets

    public class MyEditText extends EditText { // ERROR
        public MyEditText(Context context) { super(context); }
    }

    public class MyTextView extends TextView { // ERROR
        public MyTextView(Context context) { super(context); }
    }

    public class MyCheckBox extends CheckBox { // ERROR
        public MyCheckBox(Context context) { super(context); }
    }

    public class MyCheckedTextView extends CheckedTextView { // ERROR
        public MyCheckedTextView(Context context) { super(context); }
    }

    public class MyImageButton extends ImageButton { // ERROR
        public MyImageButton(Context context) { super(context); }
    }

    public class MyImageView extends ImageView { // ERROR
        public MyImageView(Context context) { super(context); }
    }

    public class MyMultiAutoCompleteTextView extends MultiAutoCompleteTextView { // ERROR
        public MyMultiAutoCompleteTextView(Context context) { super(context); }
    }

    public class MyAutoCompleteTextView extends AutoCompleteTextView { // ERROR
        public MyAutoCompleteTextView(Context context) { super(context); }
    }

    public class MyRadioButton extends RadioButton { // ERROR
        public MyRadioButton(Context context) { super(context); }
    }

    public class MyRatingBar extends RatingBar { // ERROR
        public MyRatingBar(Context context) { super(context); }
    }

    public class MySeekBar extends SeekBar { // ERROR
        public MySeekBar(Context context) { super(context); }
    }

    public class MySpinner extends Spinner { // ERROR
        public MySpinner(Context context) { super(context); }
    }

    // No current appcompat delegates

    public class MyCalendarView extends CalendarView { // OK
        public MyCalendarView(Context context) { super(context); }
    }

    public class MyChronometer extends Chronometer { // OK
        public MyChronometer(Context context) { super(context); }
    }
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You can also visit the
[source code](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/AppCompatCustomViewDetectorTest.kt)
for the unit tests for this check to see additional scenarios.

The above example was automatically extracted from the first unit test
found for this lint check, `AppCompatCustomViewDetector.test`.
To report a problem with this extracted sample, visit
https://issuetracker.google.com/issues/new?component=192708.

(##) Suppressing

You can suppress false positives using one of the following mechanisms:

* Using a suppression annotation like this on the enclosing
  element:

  ```kt
  // Kotlin
  @Suppress("AppCompatCustomView")
  fun method() {
     problematicStatement()
  }
  ```

  or

  ```java
  // Java
  @SuppressWarnings("AppCompatCustomView")
  void method() {
     problematicStatement();
  }
  ```

* Using a suppression comment like this on the line above:

  ```kt
  //noinspection AppCompatCustomView
  problematicStatement()
  ```

* Using a special `lint.xml` file in the source tree which turns off
  the check in that folder and any sub folder. A simple file might look
  like this:
  ```xml
  &lt;?xml version="1.0" encoding="UTF-8"?&gt;
  &lt;lint&gt;
      &lt;issue id="AppCompatCustomView" severity="ignore" /&gt;
  &lt;/lint&gt;
  ```
  Instead of `ignore` you can also change the severity here, for
  example from `error` to `warning`. You can find additional
  documentation on how to filter issues by path, regular expression and
  so on
  [here](https://googlesamples.github.io/android-custom-lint-rules/usage/lintxml.md.html).

* In Gradle projects, using the DSL syntax to configure lint. For
  example, you can use something like
  ```gradle
  lintOptions {
      disable 'AppCompatCustomView'
  }
  ```
  In Android projects this should be nested inside an `android { }`
  block.

* For manual invocations of `lint`, using the `--ignore` flag:
  ```
  $ lint --ignore AppCompatCustomView ...`
  ```

* Last, but not least, using baselines, as discussed
  [here](https://googlesamples.github.io/android-custom-lint-rules/usage/baselines.md.html).

<!-- Markdeep: --><style class="fallback">body{visibility:hidden;white-space:pre;font-family:monospace}</style><script src="markdeep.min.js" charset="utf-8"></script><script src="https://morgan3d.github.io/markdeep/latest/markdeep.min.js" charset="utf-8"></script><script>window.alreadyProcessedMarkdeep||(document.body.style.visibility="visible")</script>